﻿using System;
using EnvDTE;
using Microsoft.VisualStudio.Shell.Interop;
using Microsoft.WowAddonStudio.FrameXml.Services;

namespace Microsoft.WowAddonStudio.FrameXml.Components.Events.ScriptManager
{
    public class EnvDTEDocumentManager : IDocumentManager
    {
        private readonly IServiceProvider serviceProvider;

        private ProjectItem projectItem;

        public EnvDTEDocumentManager(IServiceProvider serviceProvider)
        {
            if (serviceProvider == null) 
                throw new ArgumentNullException("serviceProvider");

            this.serviceProvider = serviceProvider;
        }

        public bool DoesFunctionExist(string functionName)
        {
            if (functionName == null) 
                throw new ArgumentNullException("functionName");

            Selection.StartOfDocument(false);
            bool found = Selection.FindText(@"^:b*function:b+" + functionName, (int)FindOptions.RegularExpression);
            Selection.StartOfDocument(false);

            return found;
        }

        public void CreateFunction(string functionName)
        {
            if (functionName == null) 
                throw new ArgumentNullException("functionName");

            string functionCode = String.Format("{1}function {0}(){1}{1}end{1}", functionName, Environment.NewLine);

            Document.Activate();
            Selection.EndOfDocument(false);
            Selection.Text = functionCode;

            Selection.LineUp(false, 2);
            Selection.Indent(1);
        }

        public void ShowFunction(string functionName)
        {
            if (functionName == null) 
                throw new ArgumentNullException("functionName");

            Document.Activate();
            Selection.FindText(@"^:b*function:b+" + functionName, (int) FindOptions.RegularExpression);
        }

        public bool IsValid
        {
            get { return Selection != null; }
        }

        private TextSelection Selection
        {
            get
            {
                if (Document == null)
                    return null;

                return Document.Selection as TextSelection;
            }
        }

        private Document Document
        {
            get
            {
                if (ProjectItem == null)
                    return null;
                if (ProjectItem.Document == null)
                {
                    Window window = ProjectItem.Open(null);
                    window.Visible = true;
                }

                return ProjectItem.Document;
                
            }
        }

        private ProjectItem ProjectItem
        {
            get
            {
                if (projectItem == null)
                    projectItem = InitializeProjectItem();

                return projectItem;
            }
        }

        private ProjectItem InitializeProjectItem()
        {
            var designerService = (IFrameXmlDesignerService)serviceProvider.GetService(typeof(IFrameXmlDesignerService));
            var rdt = (IVsRunningDocumentTable)serviceProvider.GetService(typeof(SVsRunningDocumentTable));

            if (designerService == null || rdt == null)
                return null;

            IVsHierarchy projectHierarchy;
            uint xmlItemid;
            IntPtr xmlDocData;
            uint dwCookie;
            rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, designerService.DocumentMoniker,
                                    out projectHierarchy,
                                    out xmlItemid, out xmlDocData, out dwCookie);

            object firstChildValue;
            projectHierarchy.GetProperty(xmlItemid, (int)__VSHPROPID.VSHPROPID_FirstChild, out firstChildValue);

            if (firstChildValue == null)
                return null;

            var fileHandle = (int)firstChildValue;
            if (fileHandle < 0)
                return null;

            object firstChildObject;
            projectHierarchy.GetProperty((uint)fileHandle, (int)__VSHPROPID.VSHPROPID_ExtObject, out firstChildObject);

            return (ProjectItem)firstChildObject;
        }

        [Flags]
        protected enum FindOptions
        {
            None = 0,
            RegularExpression = vsFindOptions.vsFindOptionsRegularExpression,
            Backwards = vsFindOptions.vsFindOptionsBackwards
        }
    }
}
